{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Language servers\n",
    "\n",
    "By default `jupyter-lsp` does not come with any language servers preinstalled.\n",
    "However, we will try to use them if they _are_ installed and we know about them\n",
    "(i.e. someone contributed a full specification).\n",
    "\n",
    "> You can disable auto-detection by configuring\n",
    "> [autodetect](./Configuring.html#autodetect)\n",
    "\n",
    "You can add another language server for languages that are not listed on this\n",
    "page:\n",
    "\n",
    "- using a minimal JSON or Python\n",
    "  [configuration file](./Configuring.html#language-servers) (good for\n",
    "  experimenting or configuring a niche server), or\n",
    "- contributing a [full specification](./Contributing.html#specs) (to enable\n",
    "  better integration and help other users of the same language)\n",
    "\n",
    "The existing language servers are listed on the [official\n",
    "list][lsp-implementations] and on the [community-curated list][langserver].\n",
    "\n",
    "For the language servers in the tables below, use one of the suggested package\n",
    "managers to install them: these implementations are tested to work with\n",
    "`jupyter-lsp`.\n",
    "\n",
    "[language-server]:\n",
    "  https://microsoft.github.io/language-server-protocol/specification\n",
    "[langserver]: https://langserver.org\n",
    "[lsp-implementations]:\n",
    "  https://microsoft.github.io/language-server-protocol/implementors/servers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-cell"
    ]
   },
   "outputs": [],
   "source": [
    "import pathlib\n",
    "\n",
    "import IPython\n",
    "from jinja2 import Template\n",
    "\n",
    "from jupyter_lsp import LanguageServerManager"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-cell"
    ]
   },
   "outputs": [],
   "source": [
    "mgr = LanguageServerManager(extra_node_roots=[str(pathlib.Path.cwd().parent)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-cell"
    ]
   },
   "outputs": [],
   "source": [
    "mgr.init_language_servers()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-cell"
    ]
   },
   "outputs": [],
   "source": [
    "def lang_server_table(specs):\n",
    "    return IPython.display.HTML(\n",
    "        Template(\n",
    "            \"\"\"\n",
    "        <table class=\"langservers\">\n",
    "            <thead>\n",
    "                <tr>\n",
    "                    <th>Languages</th>\n",
    "                    <th>Implementation</th>\n",
    "                    <th>Installation</th>\n",
    "                </tr>\n",
    "            </thead>\n",
    "            <tbody>\n",
    "                {% for key, spec in specs.items() %}\n",
    "                <tr>\n",
    "                    <th>\n",
    "                        {% for lang in spec.languages %}\n",
    "                            <a name=\"language-{{lang}}\"/>{{ lang }}<br/>\n",
    "                        {% endfor %}\n",
    "                    </th>\n",
    "                    <td>\n",
    "                        <a href=\"{{spec.urls.home}}\">{{key}}</a>\n",
    "                    </td>\n",
    "                    <td>\n",
    "                        <ul>\n",
    "                        {% for pkgmgr, inst in spec.install.items() %}\n",
    "                        <li>{{pkgmgr}}: <code>{{ inst }}</code></li>\n",
    "                        {% endfor %}\n",
    "                        </ul>\n",
    "                    </td>\n",
    "                </tr>\n",
    "                {% endfor %}\n",
    "            </tbody>\n",
    "        </table>\n",
    "        \"\"\"\n",
    "        ).render(specs=specs)\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Servers tested with Notebooks\n",
    "\n",
    "While most servers should work with notebooks and standalone files, some require\n",
    "additional adjustments. The following language servers are tested against\n",
    "`jupyterlab-lsp` and any issues when using them in notebooks will be\n",
    "prioritised.\n",
    "\n",
    "If you choose to install multiple language servers for the same language, the\n",
    "one with the highest `priority` (which can be set in the _Advanced Settings\n",
    "Editor_) will be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "wide-table"
    ]
   },
   "outputs": [],
   "source": [
    "nb_langs = [\n",
    "    \"pylsp\",\n",
    "    \"r-languageserver\",\n",
    "    \"julia-language-server\",\n",
    "    \"jedi-language-server\",\n",
    "    # \"robotframework_ls\",\n",
    "]\n",
    "lang_server_table(\n",
    "    {\n",
    "        key: spec\n",
    "        for key, spec in sorted(mgr.all_language_servers.items())\n",
    "        if key in nb_langs\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you plan to add a custom language server for the use with notebooks, please\n",
    "note that a complete set of information should be provided by the kernel, as\n",
    "described in\n",
    "[making custom servers work with notebooks](./Configuring.html#making-custom-servers-work-with-notebooks)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Servers tested with file editor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Servers requiring Node.js\n",
    "\n",
    "These servers have mostly been tested with file editors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "wide-table"
    ]
   },
   "outputs": [],
   "source": [
    "npm_specs = {\n",
    "    key: spec\n",
    "    for key, spec in sorted(mgr.all_language_servers.items())\n",
    "    if \"npm\" in spec[\"install\"]\n",
    "}\n",
    "lang_server_table(npm_specs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NodeJS (preferably even-numbered an _Active_ or _Maintenance Long Term Support_\n",
    "release) is a prerequisite for installation of any of the above language\n",
    "servers; you can get it with:\n",
    "\n",
    "```bash\n",
    "conda install --channel conda-forge nodejs\n",
    "# or one of the following, as an administrator\n",
    "choco install nodejs            # Windows with Chocolatey\n",
    "sudo apt-get install nodejs     # Debian/Ubuntu\n",
    "sudo brew install nodejs        # MacOS with Homebrew\n",
    "sudo dnf install nodejs         # Fedora\n",
    "sudo yum install nodejs         # RHEL/CentOS\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Example: getting all the Node.js-based language servers\n",
    "\n",
    "A number of language servers are built on the\n",
    "[reference implementation](https://github.com/microsoft/vscode-languageserver-node),\n",
    "powered by Node.js. The most reliable place to install these is in a\n",
    "`node_modules` in the directory where you launch `jupyter lab`.\n",
    "\n",
    "For example, to install all the servers which are tested as part of\n",
    "`jupyterlab-lsp`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "IPython.display.Markdown(\n",
    "    Template(\n",
    "        \"\"\"```bash\n",
    "jlpm add --dev {% for name, spec in specs.items() %} \\\\\n",
    "    {{ spec[\"install\"][\"npm\"].split(\" \")[-1] }}{% endfor %}\n",
    "```\n",
    "\"\"\"\n",
    "    ).render(specs=npm_specs)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This will create (or add to):\n",
    "\n",
    "- `package.json` (check this in!)\n",
    "- `yarn.lock` (check this in!)\n",
    "- `node_modules/` (add to your VCS ignore file)\n",
    "\n",
    "If you wish to install these someplace else, you may need to specify where you\n",
    "install them with [extra_node_roots](./Configuring.html#extra_node_roots)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Standalone servers\n",
    "\n",
    "These servers have been mostly tested with file editor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "wide-table"
    ]
   },
   "outputs": [],
   "source": [
    "sci_langs = [\"texlab\"]\n",
    "lang_server_table(\n",
    "    {key: spec for key, spec in mgr.all_language_servers.items() if key in sci_langs}\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Example: Getting a $\\LaTeX$ stack\n",
    "\n",
    "```bash\n",
    "conda install --channel conda-forge tectonic texlab chktex\n",
    "```\n",
    "\n",
    "This will install:\n",
    "\n",
    "- `tectonic`, a cross-platform $\\LaTeX$ processing tool\n",
    "  - note, it will download a large number of packages when first executed\n",
    "- `texlab`, a Language Server for `.tex` files that supports completion and\n",
    "  reference navigation\n",
    "- `chktex`, a `.tex` style checker"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Community-supported servers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Servers and extensions listed below are not included in the testing suite of\n",
    "`jupyterlab-lsp`; the support may be provided by community members or a\n",
    "third-party:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Languages               | Implementation                            | Comment |\n",
    "| ----------------------- | ----------------------------------------- | ------- |\n",
    "| Spark SQL               | [CybercentreCanada/jupyterlab-sql-editor] |         |\n",
    "| GraphQL, SPARQL, Turtle | [jupyrdf/graph-lsp]                       |         |\n",
    "| JSON                    | [jupyter-lsp/json-lsp]                    | (1)     |\n",
    "| YAML                    | [jupyter-lsp/yaml-lsp]                    | (1)     |\n",
    "| Scala                   | [scalameta/metals]                        | (2)     |\n",
    "\n",
    "[jupyrdf/graph-lsp]: https://github.com/jupyrdf/graph-lsp\n",
    "[cybercentrecanada/jupyterlab-sql-editor]:\n",
    "  https://github.com/CybercentreCanada/jupyterlab-sql-editor\n",
    "[jupyter-lsp/json-lsp]: https://github.com/jupyter-lsp/json-lsp\n",
    "[jupyter-lsp/yaml-lsp]: https://github.com/jupyter-lsp/yaml-lsp\n",
    "[scalameta/metals]: https://github.com/scalameta/metals"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- (1) convenience wrappers for installation with `pip`, require Node.js.\n",
    "- (2) `metals` is not currently auto-detected, but can be configured as\n",
    "  demonstrated in the\n",
    "  [configuration example](./Configuring.html#example-scala-language-server-metals-integration)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Troubleshooting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "wide-table"
    ]
   },
   "outputs": [],
   "source": [
    "troubleshooting_data = {\n",
    "    key: spec\n",
    "    for key, spec in mgr.all_language_servers.items()\n",
    "    if (\n",
    "        \"troubleshoot\" in spec\n",
    "        # ignore trivial Node.js advice if only this is present\n",
    "        and spec[\"troubleshoot\"] != \"Node.js is required to install this server.\"\n",
    "        # ignore trivial shell advice if only this is present\n",
    "        and spec[\"troubleshoot\"] != f\"{spec['argv'][0]} not found.\"\n",
    "    )\n",
    "}\n",
    "\n",
    "IPython.display.HTML(\n",
    "    Template(\n",
    "        \"\"\"\n",
    "    {% for key, spec in specs.items() %}\n",
    "    <h4>{{ key }}</h4>\n",
    "    <p style=\"white-space: pre-wrap\">{{ spec.troubleshoot }}</p>\n",
    "    {% endfor %}\n",
    "    \"\"\"\n",
    "    ).render(specs=troubleshooting_data)\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
